namespace KMS.Core
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    [Serializable]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public class RelationAttribute : Attribute
    {
        public RelationAttribute(Type childType, string parentPropertyName, string childPropertyName)
            : this(childType, string.Empty, parentPropertyName, childPropertyName) { }

        public RelationAttribute(Type childType, string key, string parentPropertyName, string childPropertyName)
            : this(childType, key, new string[] { parentPropertyName }, new string[] { childPropertyName }) { }

        public RelationAttribute(Type childType, string[] parentPropertyName, string[] childPropertyName)
            : this(childType, string.Empty, parentPropertyName, childPropertyName) { }

        public RelationAttribute(Type childType, string key, string[] parentPropertyName, string[] childPropertyName)
            : this(null, childType, key, parentPropertyName, childPropertyName) { }

        public RelationAttribute(Type parentType, Type childType, string parentPropertyName, string childPropertyName)
            : this(parentType, childType, string.Empty, new[] { parentPropertyName }, new[] { childPropertyName }) { }

        public RelationAttribute(Type parentType, Type childType, string[] parentPropertyName, string[] childPropertyName)
            : this(parentType, childType, string.Empty, parentPropertyName, childPropertyName) { }

        public RelationAttribute(Type parentType, Type childType, string key, string[] parentPropertyName, string[] childPropertyName)
        {
            this.ParentType = parentType;
            this.ChildType = childType;
            this.ParentPropertyNames = parentPropertyName;
            this.ChildPropertyNames = childPropertyName;
            this.Key = key;
        }
        public Type ParentType { get; internal set; }
        public Type ChildType { get; private set; }
        public string[] ParentPropertyNames { get; private set; }
        public string[] ChildPropertyNames { get; private set; }
        public string Key { get; private set; }
        public string ParentTableName { get; private set; }
        public string ChildTableName { get; private set; }

        //[NonSerialized]
        internal List<PropertyAccessorImp> parentPropertyAccessors = new List<PropertyAccessorImp>();
        //[NonSerialized]
        internal List<PropertyAccessorImp> childPropertyAccessors = new List<PropertyAccessorImp>();

        internal bool Loaded { get; set; }
        public void Ensure()
        {
            if (string.IsNullOrWhiteSpace(this.Key))
            {
                this.Key = CreateDefaultKey(this.ParentType, this.ChildType);
            }
            this.ParentTableName = this.ParentType.GetTableName();
            this.ChildTableName = this.ChildType.GetTableName();

            var parentProperties = PropertyAccessorImp.ToPropertyAccessors(this.ParentType);
            var childProperties = PropertyAccessorImp.ToPropertyAccessors(this.ChildType);
            for (int i = 0; i < this.ParentPropertyNames.Length; i++)
            {
                var pName = this.ParentPropertyNames[i];
                var cName = this.ChildPropertyNames[i];

                var ppi = parentProperties.Single(x => x.Att.PropertyInfoName == pName);
                var cpi = childProperties.Single(x => x.Att.PropertyInfoName == cName);

                this.parentPropertyAccessors.Add(ppi);
                this.childPropertyAccessors.Add(cpi);
            }
            this.DataAdapter = new DapperDataAdapter(this);
        }
        public void EnsureDynamicType()
        {
            if (!this.ParentType.GetInterfaces().Any(x => x == typeof(IContainerHolder)))
            {
                this.ParentType = ILGeneratorUtil.InjectionInterfaceWithProperty(this.ParentType);
            }
            if (!this.ChildType.GetInterfaces().Any(x => x == typeof(IContainerHolder)))
            {
                this.ChildType = ILGeneratorUtil.InjectionInterfaceWithProperty(this.ChildType);
            }
        }

        [NonSerialized]
        internal DapperDataAdapter DataAdapter;
        static string CreateDefaultKey(Type parentType, Type childType)
        {
            return string.Format("{0}.{1}", parentType.Name, childType.Name);
        }
    }
}